﻿using System;
using System.IO;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Reflection;
using System.Collections;
using System.Collections.Generic;
using Microsoft.VisualBasic;

using RMA.OpenNURBS;
using RMA.Rhino;

using Grasshopper;
using Grasshopper.Kernel.Data;
using Grasshopper.Kernel.Types;

using System;

class GrasshopperSubscriber
{
    #region utility functions
    /// <summary>
    /// Print a string to the [out] field of the Script component.
    /// </summary>
    /// <param name="msg">Text to print.</param>
    private void Print(String msg)
    {
        //contents hidden in user frontend...
    }

    /// <summary>
    /// Print a formatted string to the [out] field of the Script component.
    /// </summary>
    /// <param name="s_format">Format specifier.</param>
    /// <param name="args">Formatting items.</param>
    private void Print(String s_format, params Object[] args)
    {
        //contents hidden in user frontend...
    }

    /// <summary>
    /// Examines an object instance using Reflection and prints a 
    /// summary to the [out] field of the script component.
    /// </summary>
    /// <param name="d">Object to examine.</param>
    private void Reflect(Object d)
    {
        //contents hidden in user frontend...
    }

    /// <summary>
    /// Examines a specific method of a object instance using Reflection and prints a 
    /// summary to the [out] field of the script component.
    /// </summary>
    /// <param name="d">Object to examine.</param>
    /// <param name="proc_name">Name of method to examine.</param>
    private void Reflect(Object d, String proc_name)
    {
        //contents hidden in user frontend...
    }
    #endregion

    #region members
    private MRhinoApp app;
    private MRhinoDoc doc;

    public System.Object message;
    #endregion

    bool RunScript(int port, string topic)
    {
        message = _messageText;

        //If we already had started receiving
        if (_isReceivingStarted)
        {

            if (_isMessageReceived)
            {
                //We set that message was collected
                _isMessageReceived = false;
                Print("Value is updated...");
            }
            else
            {
                Print("Loop with no updates...");
            }

            //if (port != listenPort)
            //{
            //    if (port == -1)
            //    {
            //        StopReceiving();
            //        listenPort = port;
            //    }
            //    else
            //    {
            //        StopReceiving();
            //        if (ReceiveMessages(port))
            //        {
            //            listenPort = port;
            //        }
            //    }
            //}
        }
        //Otherwise we need to start receiving...
        else
        {
            if (port > 0)
            {
                string serverIP = "127.0.0.1";
                IPAddress serverIPAddress = IPAddress.Parse(serverIP);
                int serverPort = port;
                _client = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
                _remoteEndPoint = new IPEndPoint(serverIPAddress, serverPort);  
            }
            else
            {
                Print("Waiting for a port to listen to...");
            }
        }

        //client = udpClient;

    }

    #region "Additional methods and Type declarations"

    Socket _client;
    EndPoint _remoteEndPoint;
    byte[] _data;
    int _recv;
    string _messageText;
    bool _isMessageReceived = false;
    Boolean _isReceivingStarted = false;

    //public GrasshopperSubscriber()
    //{
    //    string serverIP = "127.0.0.1";
    //    IPAddress serverIPAddress = IPAddress.Parse(serverIP);
    //    int serverPort = 10001;

    //    _client = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
    //    _remoteEndPoint = new IPEndPoint(serverIPAddress, serverPort);
    //}

    private void ReceiveDataFromServer()
    {
        EndPoint publisherEndPoint = _client.LocalEndPoint;
        while (true)
        {
            _recv = _client.ReceiveFrom(_data, ref publisherEndPoint);
            _messageText = Encoding.ASCII.GetString(_data, 0, _recv) + "," + publisherEndPoint.ToString();
            _isMessageReceived = true;
        }
    }

    public void Subscribe(string topicname)
    {
        string Command = "Subscribe";

        string message = topicName;
        _client.SendTo(Encoding.ASCII.GetBytes(message), _remoteEndPoint);

        if (_isReceivingStarted == false)
        {
            _isReceivingStarted = true;
            _data = new byte[1024];
            Thread thread1 = new Thread(new ThreadStart(ReceiveDataFromServer));
            thread1.IsBackground = true;
            thread1.Start();
        }

    }
    public void Unsubscribe(Topic.WII topicName)
    {
        string command = "UnSubscribe";

        string message = topicName;
        _client.SendTo(Encoding.ASCII.GetBytes(message), _remoteEndPoint);
    }

    public void Shutdown()
    {
        _client.Disconnect(true);
        _client.Close();
        _client = null;
        _isReceivingStarted = false;
    }


    ////Code example from http://msdn.microsoft.com/en-us/library/system.net.sockets.udpclient.beginreceive.aspx
    //private System.Net.Sockets.UdpClient udpClient;
    //private System.Net.IPEndPoint ip;

    //private int listenPort = 0;
    //private static bool begunReceiving = false;
    //private volatile bool messageReceived = false;
    //private volatile string messageText = null;

    //private void ReceiveCallback(IAsyncResult ar)
    //{
    //    try
    //    {
    //        if (udpClient != null)
    //        {
    //            Byte[] receiveBytes = udpClient.EndReceive(ar, ref ip);
    //            messageText = System.Text.Encoding.ASCII.GetString(receiveBytes);
    //            messageReceived = true;
    //            Print("Received " + messageText);

    //            udpClient.BeginReceive(new AsyncCallback(ReceiveCallback), null);
    //        }
    //    }
    //    catch (Exception ex)
    //    {

    //        Print("Error in ReceiveCallback: " + ex.Message);

    //    }
    //}

    //private bool ReceiveMessages(int port)
    //{
    //    try
    //    {

    //        //Change here the IP address
    //        ip = new System.Net.IPEndPoint(System.Net.IPAddress.Loopback
    //          , port);

    //        udpClient = new System.Net.Sockets.UdpClient(ip);

    //        udpClient.BeginReceive(new AsyncCallback(ReceiveCallback), null);

    //        begunReceiving = true;
    //        Print("Listening for messages");
    //        return true;

    //    }
    //    catch (System.Net.Sockets.SocketException ex)
    //    {

    //        if (ex.SocketErrorCode == System.Net.Sockets.SocketError.AddressAlreadyInUse)
    //        {
    //            Print("Socket error: If you changed the C# window code without setting the port to -1, you will need to restart Rhino." + ex.Message + ".");
    //        }

    //    }
    //    catch (Exception ex)
    //    {
    //        Print("Error: " + ex.Message);
    //    }

    //    return false;
    //}

    //private void StopReceiving()
    //{
    //    try
    //    {
    //        if (udpClient != null)
    //        {
    //            udpClient.Client.Shutdown(System.Net.Sockets.SocketShutdown.Both);
    //            udpClient.Close();
    //            udpClient = null;
    //        }
    //        begunReceiving = false;
    //        Print("Stopped receiving");
    //    }
    //    catch (Exception ex)
    //    {
    //        Print("Error while stopping receiving: " + ex.Message);
    //    }
    //}

    #endregion
}


public static class Topic
{
    public enum WII
    {
        A,
        B,
        Up,
        Down,
        Left,
        Right,
        Minus,
        Plus,
        Home,
        One,
        Two,
        X,
        Y,
        Z,
        ir0X,
        ir0Y,
        ir1X,
        ir1Y,
        ir2X,
        ir2Y,
        ir3X,
        ir3Y,
        irRaw0X,
        irRaw0Y,
        irRaw1X,
        irRaw1Y,
        irRaw2X,
        irRaw2Y,
        irRaw3X,
        irRaw3Y,
        ir0Size,
        ir1Size,
        ir2Size,
        ir3Size,
        ir0Found,
        ir1Found,
        ir2Found,
        ir3Found,
        LED1,
        LED2,
        LED3,
        LED4,
        Yaw,
        Pitch,
        Roll,
        ExtensionType,
        ExtensionInserted,
        Rumble,
        Battery,
        NunC,
        NunZ,
        NunXAcc,
        NunYAcc,
        NunZAcc,
        NunXJoystick,
        NunYJoystick,
        NunYaw,
        NunPitch,
        NunRoll
    }
    public enum ARDUINO
    {
        Light,
        Temperature,
        Servo
    }
}